﻿namespace Mp3Observer.Model
{
    #region usings

    using System.Collections.ObjectModel;
    using System.Collections.Specialized;
    using System.ComponentModel;
    using System.Threading;
using System;

    #endregion

    /// <summary>
    /// A ObservableCollection that can bound to UI and updated by any other Thread. (common Wpf Threading Problem Solution)
    /// Solution is to delegate the Add(=insert), clear, remove, replace(set )to the synchronizationContext
    /// </summary>
    /// <typeparam name="T">any .net type</typeparam>
    [Obsolete("slow", true)]
    public class CrossThreadObservableCollection<T> : Collection<T>, INotifyCollectionChanged, INotifyPropertyChanged
    {
        /// <summary>
        /// The SynchronizationContext (the "magic")
        /// </summary>
        private SynchronizationContext synchronizationContext;

        /// <summary>
        /// Occurs when the collection changes.
        /// </summary>
        public event NotifyCollectionChangedEventHandler CollectionChanged;

        /// <summary>
        /// Initializes a new instance of the <see cref="CrossThreadObservableCollection&lt;T&gt;"/> class.
        /// </summary>
        public CrossThreadObservableCollection()
            : this(SynchronizationContext.Current)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CrossThreadObservableCollection&lt;T&gt;"/> class.
        /// </summary>
        /// <param name="synchronizationContext">The synchronization context.</param>
        public CrossThreadObservableCollection(SynchronizationContext synchronizationContext)
        {
            this.synchronizationContext = synchronizationContext;
        }

        /// <summary>
        /// Removes all elements from the <see cref="T:System.Collections.ObjectModel.Collection`1"/>.
        /// </summary>
        protected override void ClearItems()
        {
            if (synchronizationContext != null)
                synchronizationContext.Send(delegate { ClearItemsImpl(); }, null);
            else
                ClearItemsImpl();
        }

        /// <summary>
        /// Inserts an element into the <see cref="T:System.Collections.ObjectModel.Collection`1"/> at the specified index.
        /// </summary>
        /// <param name="index">The zero-based index at which <paramref name="item"/> should be inserted.</param>
        /// <param name="item">The object to insert. The value can be null for reference types.</param>
        /// <exception cref="T:System.ArgumentOutOfRangeException">
        /// 	<paramref name="index"/> is less than zero.
        /// -or-
        /// <paramref name="index"/> is greater than <see cref="P:System.Collections.ObjectModel.Collection`1.Count"/>.
        /// </exception>
        protected override void InsertItem(int index, T item)
        {
            if (this.synchronizationContext != null)
                this.synchronizationContext.Send(delegate { this.InsertItemImpl(index, item); }, null);
            else
                this.InsertItemImpl(index, item);
        }

        /// <summary>
        /// Removes the element at the specified index of the <see cref="T:System.Collections.ObjectModel.Collection`1"/>.
        /// </summary>
        /// <param name="index">The zero-based index of the element to remove.</param>
        /// <exception cref="T:System.ArgumentOutOfRangeException">
        /// 	<paramref name="index"/> is less than zero.
        /// -or-
        /// <paramref name="index"/> is equal to or greater than <see cref="P:System.Collections.ObjectModel.Collection`1.Count"/>.
        /// </exception>
        protected override void RemoveItem(int index)
        {
            if (synchronizationContext != null)
                this.synchronizationContext.Send(delegate { this.RemoveItemImpl(index); }, null);
            else
                this.RemoveItemImpl(index);
        }


        /// <summary>
        /// Replaces the element at the specified index.
        /// </summary>
        /// <param name="index">The zero-based index of the element to replace.</param>
        /// <param name="item">The new value for the element at the specified index. The value can be null for reference types.</param>
        /// <exception cref="T:System.ArgumentOutOfRangeException">
        /// 	<paramref name="index"/> is less than zero.
        /// -or-
        /// <paramref name="index"/> is greater than <see cref="P:System.Collections.ObjectModel.Collection`1.Count"/>.
        /// </exception>
        protected override void SetItem(int index, T item)
        {
            if (this.synchronizationContext != null)
                this.synchronizationContext.Send(delegate { this.SetItemImpl(index, item); }, null);
            else
                this.SetItemImpl(index, item);
        }

        /// <summary>
        /// Raises the <see cref="E:CollectionChanged"/> event.
        /// </summary>
        /// <param name="e">The <see cref="System.Collections.Specialized.NotifyCollectionChangedEventArgs"/> instance containing the event data.</param>
        protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            this.OnPropertyChanged("Count");
            var collectionChanged = this.CollectionChanged;
            if (collectionChanged != null)
                collectionChanged(this, e);
        }

        #region INotifyPropertyChanged Members

        /// <summary>
        /// Occurs when a property value changes.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Called when [property changed].
        /// </summary>
        /// <param name="propertyName">Name of the property.</param>
        protected virtual void OnPropertyChanged(string propertyName)
        {
            var prop = this.PropertyChanged;
            if (prop != null)
                prop(this, new PropertyChangedEventArgs(propertyName));
        }

        #endregion

        /// <summary>
        /// Implementation of Clear.
        /// </summary>
        private void ClearItemsImpl()
        {
            base.ClearItems();
            this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        }

        /// <summary>
        /// Implementation of Insert.
        /// </summary>
        /// <param name="index">The index.</param>
        /// <param name="item">The item.</param>
        private void InsertItemImpl(int index, T item)
        {
            base.InsertItem(index, item);
            this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index));
        }

        /// <summary>
        /// Implementation of Remove.
        /// </summary>
        /// <param name="index">The index.</param>
        private void RemoveItemImpl(int index)
        {
            T item = this[index];
            base.RemoveItem(index);
            this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index));
        }

        /// <summary>
        /// Implementation of Set.
        /// </summary>
        /// <param name="index">The index.</param>
        /// <param name="item">The item.</param>
        private void SetItemImpl(int index, T item)
        {
            T old = this[index];
            base.SetItem(index, item);
            this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, item, old, index));
        }
    }
}
